﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.CRT.Synchronization.Entities;
using Sustainalytics.ESG.Entities;
using Sustainalytics.ClientDW.Entities;

namespace Sustainalytics.CRT.Synchronization
{
    public class MarketCapitalizationProcessor
    {

        private readonly Dictionary<Guid, Tuple<string, int>> companiesGroupIndexing = new Dictionary<Guid, Tuple<string, int>>();

        private Dictionary<string, List<MarketCapInfo>> companiesIndex;

        public MarketCapitalizationProcessor(IMongoCollection<CorporateData> corporateDataCollection, IMongoCollection<RPCompanyESGComputationResult> scoresCollection, IMongoCollection<FinancialData> financialDataCollection)
        {
            InitAsync(corporateDataCollection, scoresCollection, financialDataCollection).ConfigureAwait(false).GetAwaiter().GetResult();
        }

        public void AppendPeersListToResult(RPCompanyESGComputationResult companyToPopulate, string companyPeerGroup)
        {
            if (companyToPopulate == null)
            {
                throw new ArgumentNullException(nameof(companyToPopulate));
            }

            List<MarketCapInfo> groupMarketCap;
            if (!companiesIndex.TryGetValue(companyPeerGroup, out groupMarketCap))
            {
                throw new ArgumentException(nameof(companyPeerGroup));
            }

            Tuple<string, int> marketCapCompanyIndex;
            if (!companiesGroupIndexing.TryGetValue(companyToPopulate.Id, out marketCapCompanyIndex))
            {
                throw new IndexOutOfRangeException(companyToPopulate.Id.ToString());
            }

            var peerInfo = new MarketCapitalizationManager(groupMarketCap, marketCapCompanyIndex.Item2).GetCompanyPeersInformation(companyToPopulate);

            companyToPopulate.RelativePosition.MarketCapRangeFrom = peerInfo.Min;
            companyToPopulate.RelativePosition.MarketCapRangeTo = peerInfo.Max;
            companyToPopulate.RelativePosition.PeersList = peerInfo.Peers;
        }

        private async Task InitAsync(IMongoCollection<CorporateData> corporateDataCollection, IMongoCollection<RPCompanyESGComputationResult> scoresCollection, IMongoCollection<FinancialData> financialDataCollection )
        {
            var companyData = new ConcurrentBag<MarketCapInfo>();
            await corporateDataCollection.Find(
                           x =>
                               x.CoreEsgResearchState.ResearchState == ResearchState.ResearchAvailable ||
                               x.ComprehensiveEsgResearchState.ResearchState == ResearchState.ResearchAvailable)
                       .Project(
                           x =>
                               new
                               {
                                   x.Id,
                                   x.CompanyName,
                                   x.PeerGroup,
                                   ComprehensiveResearch = x.ComprehensiveEsgResearchState,
                                   CoreRatingsResearch = x.CoreEsgResearchState
                               })
                       .ForEachAsync(async (researchState) =>
                       {
                           var result = new MarketCapInfo
                           {
                               Id = researchState.Id,
                               CompanyName = researchState.CompanyName,
                               HasEsgResearch = researchState.ComprehensiveResearch.ResearchState == ResearchState.ResearchAvailable,
                               HasFocusedRatingsReasearch = researchState.CoreRatingsResearch.ResearchState == ResearchState.ResearchAvailable
                           };
                           var financialData = await financialDataCollection
                                        .Find(x => x.Id == researchState.Id)
                                        .Project(x => x.MarketCapitalization)
                                        .FirstOrDefaultAsync()
                                        .ConfigureAwait(false);
                           if (researchState.ComprehensiveResearch.ResearchState == ResearchState.ResearchAvailable)
                           {
                               var score = await scoresCollection
                               .Find(x => x.Id == researchState.Id)
                               .Project(x => x.OverallPerformance.Score)
                               .FirstOrDefaultAsync()
                               .ConfigureAwait(false);
                               result.Score = score;
                           }
                           result.MarketCap = financialData.GetValueOrDefault();
                           result.PeerGroup = researchState.PeerGroup;
                           companyData.Add(result);
                       })
                       .ConfigureAwait(false);

            companiesIndex = companyData.GroupBy(x => x.PeerGroup)
                  .ToDictionary(group => group.Key, value => value.OrderByDescending(x => x.MarketCap).ToList());

            foreach (var group in companiesIndex)
            {
                var index = 0;
                foreach (var company in group.Value)
                {
                    companiesGroupIndexing.Add(company.Id, new Tuple<string, int>(group.Key, index++));
                }
            }

        }

    }
}